package com.cloudea.lotus;

import com.cloudea.lotus.lambda.LotusLambda;
import com.cloudea.lotus.utils.GsonUtil;
import com.cloudea.lotus.utils.ParameterParser;
import com.sun.net.httpserver.*;
import org.apache.log4j.Logger;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URLDecoder;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * 服务器对象（装饰器模式）
 */
public class Server {
    private static Logger logger = Logger.getLogger(Server.class);

    // HTTP Server
    private HttpServer httpServer;
    private int port;
    private final String ENCODING = "utf-8";

    /**
     * 构造函数 如果无法成功构造则退出程序
     * @param port 端口号
     */
    public Server(int port){
        try {
            this.port = port;
            httpServer = serverFactory("http", port);
            BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(200);
            Executor executor = new ThreadPoolExecutor(100, 200, 1, TimeUnit.DAYS, queue);
            httpServer.setExecutor(executor);
        } catch (IOException e) {
            logger.trace("服务异常", e);
            System.exit(-1);
        }
    }

    /**
     * 添加处理上下文
     * @param object  数据对象的名字
     * @param lotusLambda  上下文处理函数
     */
    public void addContext(String object, final LotusLambda lotusLambda){

        httpServer.createContext("/" + object, new HttpHandler() {
            private final ParameterParser parameterParser = new ParameterParser();

            public void handle(HttpExchange httpExchange) throws IOException {
                Headers headers = httpExchange.getResponseHeaders();
                headers.add("Content-Type", "application/json");

                /* 添加跨域支持
                - 再次发option请求的间隔为1个小时
                - 如果请求中有Origin，则返回同样的Origin，并且允许cookie
                - 如果请求中没有Origin，则允许所有的，并且不允许cookie（因为当Access-Control-Allow-Credentials=true时，origin不能为*）
                 */
                List<String> origins = httpExchange.getRequestHeaders().get("Origin");
                headers.add("Access-Control-Allow-Origin", (origins != null && origins.size() > 0) ? origins.get(0) : "*");
                headers.add("Access-Control-Allow-Methods", "*");
                headers.add("Access-Control-Max-Age", "3600");
                headers.add("Access-Control-Allow-Headers", "*");
                headers.add("Access-Control-Allow-Credentials", (origins != null && origins.size() > 0) ? "true" : "false");

                httpExchange.sendResponseHeaders(200, 0);
                String method = httpExchange.getRequestMethod().toUpperCase();
                String path = httpExchange.getRequestURI().getPath();
                String query = httpExchange.getRequestURI().getQuery();
                String body = new String(httpExchange.getRequestBody().readAllBytes(), ENCODING);
                Map<String, String> queryParameters = parameterParser.parse(URLDecoder.decode(query != null ? query : "", ENCODING));
                Map<String, String> bodyParameters =  parameterParser.parse(URLDecoder.decode(body, ENCODING));
                queryParameters.putAll(bodyParameters);
                // 根据不同的method调用不同的回调函数
                Request request = new Request(path, queryParameters);
                Response response = null;
                if(method.equals("GET")){
                    response = lotusLambda.get(request);
                }else if(method.equals("POST")){
                    response = lotusLambda.post(request);
                }else if(method.equals("HEAD")){
                    response = lotusLambda.head(request);
                }else if(method.equals("PUT")){
                    response = lotusLambda.put(request);
                }else if(method.equals("DELETE")){
                    response = lotusLambda.delete(request);
                }else if(method.equals("PATCH")){
                    response = lotusLambda.patch(request);
                }else if(method.equals("CONNECT")){
                    response = lotusLambda.connect(request);
                }else{
                    response  = Response.fail("错误的请求方式");
                }
                //返回json字符串
                String json = GsonUtil.toJson(response);
                httpExchange.getResponseBody().write(json.getBytes(ENCODING));
                httpExchange.getResponseBody().close();
            }
        });
    }

    /**
     * 启动HTTP服务器
     */
    public void start(){
        httpServer.start();
        logger.info("服务运行在端口:" + port);
    }

    /**
     * 简单工厂，把回一个HTTP服务器实例
     * @param serverType 服务有类型  http、https
     * @param port      运行的端口号
     * @return 一个HTTPServer对象
     * @throws IOException
     */
    public static HttpServer serverFactory(String serverType, int port) throws IOException {
        if(serverType.equals("http")){
            return HttpServer.create(new InetSocketAddress(port), 0);
        }else if(serverType.equals("https")){
            // 此处应该设SSL的上下文，但此处省略，只作为扩展的演示
            // HttpsServer hs = null;
            // hs.setHttpsConfigurator(new HttpsConfigurator());
            return HttpsServer.create(new InetSocketAddress(port), 0);
        }else {
            throw new RuntimeException("错误的服务类型");
        }
    }
}
